using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using Xfinity.Helpers;
using Microsoft.Xna.Framework;
using Xfinity.Console;

namespace Xfinity.Loaders
{
    public struct Asset
    {
        public struct UnitMeasurement
        {
            public string Name;
            public float Meter;
        }
        public struct Contributor
        {
            public string Author;
            public string AuthoringTool;
            public string Comments;
            public string Copyright;
            public string SourceData;
        }
        public DateTime Created;
        public Contributor[] Contributors;
        public string Keywords;
        public DateTime Modified;
        public string Revision;
        public string Subject;
        public string Title;
        public UnitMeasurement Unit;
        public Vector3 UpAxis;
    }

    public class ColladaContainer
    {
        private Asset asset;

        /// <summary>
        /// 
        /// </summary>
        public Asset Asset
        {
            get { return asset; }
            set { asset = value; }
        }

        public static void ProcessColladaDocument(XmlNode node)
        {
            FindAndProcessAssetNode(node);
            foreach (XmlNode childNode in node)
            {
                if (childNode.Name == "asset")
                {
                    continue;
                }
                switch (childNode.Name)
                {
                    case "library_animations":
                        {
                            //ProcessAnimationLibrary(childNode);
                            break;
                        }
                    case "library_animation_clips":
                        {
                            //ProcessAnimationClipLibrary(childNode);
                            break;
                        }
                    case "library_cameras":
                        {
                            //ProcessCameraLibrary(childNode);
                            break;
                        }
                    case "library_controllers":
                        {
                            //ProcessControllerLibrary(childNode);
                            break;
                        }
                    case "library_effects":
                        {
                            //ProcessEffectLibrary(childNode);
                            break;
                        }
                    case "library_force_fields":
                        {
                            //ProcessForceFieldLibrary(childNode);
                            break;
                        }
                    case "library_geometries":
                        {
                            ProcessGeometryLibrary(childNode);
                            break;
                        }
                    case "library_images":
                        {
                            //ProcessImageLibrary(childNode);
                            break;
                        }
                    case "library_lights":
                        {
                            //ProcessLightLibrary(childNode);
                            break;
                        }
                    case "library_materials":
                        {
                            //ProcessMaterialLibrary(childNode);
                            break;
                        }
                    case "library_nodes":
                        {
                            //ProcessNodeLibrary(childNode);
                            break;
                        }
                    case "library_physics_materials":
                        {
                            //ProcessPhysicsMaterialLibrary(childNode);
                            break;
                        }
                    case "library_physics_models":
                        {
                            //ProcessPhysicsModelLibrary(childNode);
                            break;
                        }
                    case "library_physics_scenes":
                        {
                            //ProcessPhysicsSceneLibrary(childNode);
                            break;
                        }
                    case "library_visual_scenes":
                        {
                            //ProcessVisualSceneLibrary(childNode);
                            break;
                        }
                    case "scene":
                        {
                            //ProcessSceneNode(childNode);
                            break;
                        }
                    case "extra":
                        {
                            ProcessExtraNode(childNode);
                            break;
                        }
                }
            }
        }

        private static string ProcessExtraNode(XmlNode childNode)
        {
            return childNode.InnerXml;
            //throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessEffectLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessControllerLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessForceFieldLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessImageLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessLightLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessMaterialLibrary(XmlNode libraryNode)
        {
            FindAndProcessAssetNode(libraryNode);
            foreach (XmlNode materialNode in FindChildNodes(libraryNode, "material"))
            {
                ProcessMaterialNode(materialNode);
            }
            FindAndProcessExtraNodes(libraryNode);
        }

        private static void ProcessMaterialNode(XmlNode materialNode)
        {
            FindAndProcessAssetNode(materialNode);
            ProcessInstanceEffectNode(materialNode["instance_effect"]);
            FindAndProcessExtraNodes(materialNode);
        }

        private static void ProcessInstanceEffectNode(XmlNode node)
        {
            string url = node.Attributes["url"].Value;
        }

        private static List<string> FindAndProcessExtraNodes(XmlNode libraryNode)
        {
            List<string> list = new List<string>();
            foreach (XmlNode extraNode in FindChildNodes(libraryNode, "extra"))
            {
                list.Add(ProcessExtraNode(extraNode));
            }
            return list;
        }

        private static void ProcessNodeLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessPhysicsMaterialLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessPhysicsModelLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessPhysicsSceneLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessVisualSceneLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessSceneNode(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessCameraLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessAnimationClipLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessAnimationLibrary(XmlNode childNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        #region Asset node
        private static void FindAndProcessAssetNode(XmlNode node)
        {
            XmlNode assetNode = node["asset"];
            if (assetNode == null)
            {
                return;
            }
            ProcessAssetNode(assetNode);
        }
        private static Asset ProcessAssetNode(XmlNode assetNode)
        {
            Asset asset = new Asset();
            XmlNode[] contributorNodes = FindChildNodes(assetNode, "contributor");
            asset.Contributors = ProcessContributorNodes(contributorNodes);
            SetValue(assetNode, "created", out asset.Created);
            SetValue(assetNode, "keywords", out asset.Keywords);
            SetValue(assetNode, "modified", out asset.Modified);
            SetValue(assetNode, "revision", out asset.Revision);
            SetValue(assetNode, "subject", out asset.Subject);
            SetValue(assetNode, "title", out asset.Title);
            SetValue(assetNode, "unit", out asset.Unit);
            SetUpField(assetNode, "up_axis", out asset.UpAxis);
            return asset;
        }
        private static Asset.Contributor[] ProcessContributorNodes(XmlNode[] contributorNodes)
        {
            Asset.Contributor[] contributors = new Asset.Contributor[contributorNodes.Length];
            for (int i = 0; i < contributors.Length; i++)
            {
                contributors[i] = ProcessContributorNode(contributorNodes[i]);
            }
            return contributors;
        }
        private static Asset.Contributor ProcessContributorNode(XmlNode xmlNode)
        {
            Asset.Contributor contributor = new Asset.Contributor();
            SetValue(xmlNode, "author", out contributor.Author);
            SetValue(xmlNode, "authoring_tool", out contributor.AuthoringTool);
            SetValue(xmlNode, "comments", out contributor.Comments);
            SetValue(xmlNode, "copyright", out contributor.Copyright);
            SetValue(xmlNode, "source_data", out contributor.SourceData);
            return contributor;
        }
        #endregion

        private static XmlNode[] FindChildNodes(XmlNode node, string name)
        {
            List<XmlNode> nodes = new List<XmlNode>();
            foreach (XmlNode child in node)
            {
                if (child.Name == name)
                {
                    nodes.Add(child);
                }
            }
            return nodes.ToArray();
        }
        private static void SetValue(XmlNode node, string element, out Asset.UnitMeasurement field)
        {
            XmlElement e = node[element];
            if (e != null)
            {
                field = new Asset.UnitMeasurement();
                field.Meter = Convert.ToSingle(e.Attributes["meter"].Value);
                field.Name = e.Attributes["name"].Value;
            }
            else
            {
                field = new Asset.UnitMeasurement();
                field.Meter = 1;
                field.Name = "meter";
            }
        }
        private static void SetValue(XmlNode node, string element, out string field)
        {
            XmlElement e = node[element];
            if (e != null)
            {
                field = node[element].Value;
            }
            else
            {
                field = "";
            }
        }
        private static void SetUpField(XmlNode node, string element, out Vector3 field)
        {
            XmlElement e = node[element];
            if (e != null)
            {
                string value = node[element].Value;
                switch (value)
                {
                    case "X_UP":
                        {
                            field = Vector3.UnitX;
                            break;
                        }
                    case "Y_UP":
                        {
                            field = Vector3.UnitY;
                            break;
                        }
                    case "Z_UP":
                        {
                            field = Vector3.UnitZ;
                            break;
                        }
                    default:
                        field = Vector3.UnitY;
                        break;
                }
            }
            else
            {
                field = Vector3.UnitY;
            }
        }
        private static void SetValue(XmlNode node, string element, out DateTime field)
        {
            XmlElement e = node[element];
            if (e != null)
            {
                field = DateTime.Parse(node[element].Value);
            }
            else
            {
                field = DateTime.Now;
            }
        }

        private static void ProcessGeometryLibrary(XmlNode geometryLibrary)
        {
            FindAndProcessAssetNode(geometryLibrary);
            foreach (XmlNode geometryNode in FindChildNodes(geometryLibrary, "geometry"))
            {
                ProcessGeometryNode(geometryNode);
            }
            FindAndProcessExtraNodes(geometryLibrary);
        }

        private static void ProcessGeometryNode(XmlNode geometryNode)
        {
            foreach (XmlNode innerNode in geometryNode)
            {
                switch (innerNode.Name)
                {
                    case "asset":
                        {
                            ProcessAssetNode(innerNode);
                            break;
                        }
                    case "convex_mesh":
                        {
                            ProcessConvexMeshNode(innerNode);
                            break;
                        }
                    case "mesh":
                        {
                            ProcessMeshNode(innerNode);
                            break;
                        }
                    case "spline":
                        {
                            ProcessSplineNode(innerNode);
                            break;
                        }
                    case "extra":
                        {
                            ProcessExtraNode(innerNode);
                            break;
                        }
                }
            }
        }

        private static void ProcessSplineNode(XmlNode innerNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void ProcessMeshNode(XmlNode innerNode)
        {
            XmlNode[] sourceLists = FindChildNodes(innerNode, "source");

            foreach (XmlNode sourceNode in sourceLists)
            {
                ProcessSourceNode(sourceNode);
            }
        }

        private static object ProcessSourceNode(XmlNode sourceNode)
        {
            FindAndProcessAssetNode(sourceNode);
            string id = sourceNode.Attributes["id"].Value;
            ObjectArray a;
            SourceData data = new SourceData();
            data.ID = id;
            if (FindChildNodes(sourceNode, "IDREF_array").Length != 0)
            {
                throw new NotImplementedException();
                ProcessIdRefArray(XmlHelper.FindNode(sourceNode, "IDREF_array"));
            }
            else if (FindChildNodes(sourceNode, "Name_array").Length == 1)
            {
                throw new NotImplementedException();
                a = ProcessArray<Name>(XmlHelper.FindNode(sourceNode, "name_array"));
                data.ArrayType = typeof(Name);
            }
            else if (FindChildNodes(sourceNode, "bool_array").Length == 1)
            {
                a = ProcessArray<bool>(XmlHelper.FindNode(sourceNode, "bool_array"));
                data.ArrayType = typeof(bool);
            }
            else if (FindChildNodes(sourceNode, "float_array").Length == 1)
            {
                a = ProcessArray<float>(XmlHelper.FindNode(sourceNode, "float_array"));
                data.ArrayType = typeof(float);
            }
            else if (FindChildNodes(sourceNode, "int_array").Length == 1)
            {
                a = ProcessArray<int>(XmlHelper.FindNode(sourceNode, "int_array"));
                data.ArrayType = typeof(int);
            }
            else
            {
                throw new InvalidOperationException();
            }

            XmlNode[] technodes = FindChildNodes(sourceNode, "technique_common");
            if (technodes.Length > 0)
            {
                ProcessAccessorNode(XmlHelper.FindNode(technodes[0], "accessor"));
            }


            data.Array = a;
            return data;

        }

        private static void ProcessAccessorNode(XmlNode xmlNode)
        {
            throw new Exception("The method or operation is not implemented.");
        }
        public class SourceData
        {
            private Type arrayType;

            /// <summary>
            /// 
            /// </summary>
            public Type ArrayType
            {
                get { return arrayType; }
                set { arrayType = value; }
            }

            private ObjectArray array;

            /// <summary>
            /// 
            /// </summary>
            public ObjectArray Array
            {
                get { return array; }
                set { array = value; }
            }
            private string id;

            /// <summary>
            /// 
            /// </summary>
            public string ID
            {
                get { return id; }
                set { id = value; }
            }


        }
        public class Name
        {

        }
        private static ObjectArray<T> ProcessArray<T>(XmlNode xmlNode)
        {
            ObjectArray<T> oa = new ObjectArray<T>();
            oa.ID = xmlNode.Attributes["id"].Value;
            uint count = Convert.ToUInt32(xmlNode.Attributes["count"].Value);
            oa.Count = count;
            string[] stringArray = xmlNode.InnerText.Split(' ');
            T[] array = new T[count];
            for (uint i = 0; i < count; i++)
            {
                array[i] = (T)Convert.ChangeType(stringArray[i], typeof(T));
            }

            return oa;
        }
        public class ObjectArray
        {
            private string id = String.Empty;

            /// <summary>
            /// 
            /// </summary>
            public string ID
            {
                get { return id; }
                set { id = value; }
            }
            private uint count;

            /// <summary>
            /// 
            /// </summary>
            public uint Count
            {
                get { return count; }
                set { count = value; }
            }
        }
        public class ObjectArray<T> : ObjectArray
        {
            private List<T> array = new List<T>();

            /// <summary>
            /// 
            /// </summary>
            public List<T> Array
            {
                get { return array; }
                set { array = value; }
            }
        }

        private static void ProcessIdRefArray(XmlNode xmlNode)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        private static void ProcessConvexMeshNode(XmlNode innerNode)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        private static void PrintNodeNames(XmlNode node)
        {
            foreach (XmlNode inner in node)
            {
                StandardConsole.WriteLine(inner.Name);
            }
        }


    }

    public class ColladaDocument
    {
        private XmlNode doc;

        /// <summary>
        /// 
        /// </summary>
        public XmlNode Document
        {
            get { return doc; }
            set { doc = value; }
        }

        public ColladaDocument(string path)
        {
            Stream file = File.OpenRead(path);
            string colladaXml = new StreamReader(file).ReadToEnd();
            doc = XmlHelper.LoadXmlFromText(colladaXml);
            //doc.Read();            
        }
    }
}
